/*
	Copyright (c) 2012 by karmaleaving@qq.com. All rights RESERVED.
*/
/*
	This file defines a simple encapsulation for mysql api. You should 
		add ths mysql header files and library files in your project if
		using this file.
*/

#ifndef __MYSQL_H_VERSION_KL__
#define __MYSQL_H_VERSION_KL__
#include "config.h"

#ifdef _KL_UNIX_
#include <sys/socket.h>
#else
#include <windows.h>
#pragma comment(lib, "WS2_32.LIB")
#pragma comment(lib, "libmysql.lib")
#endif // _KL_UNIX_
/* mysql header files. */ 
#include "mysql.h"
#include "errmsg.h"

KL_BEGIN
class MysqlResult
{
private:
	MYSQL_RES *data;
public:
	MysqlResult(MYSQL_RES * res)
		: data(res)
	{
	}
	size_t column_count()
	{
		if(!data) return 0;
		return (size_t)data->field_count;
	}
	my_ulonglong row_count()
	{
		if(!data) return 0;
		return (size_t)data->row_count;
	}
	bool valid() const
	{
		return data != NULL;
	}
	operator bool () const
	{
		return valid();
	}
	bool next_row()
	{
		if(!data) return false;
		return mysql_fetch_row(data) != NULL;
	}
	MYSQL_ROW current_row()
	{
		if(!data) return NULL;
		return data->current_row;
	}
	char * column(size_t num)
	{
		if(data && data->current_row && num < data->field_count)	
			return data->current_row[num];
		else return NULL;
	}		
	MYSQL_FIELD * fields(size_t num)
	{
		if(data && num < data->field_count)	
			return &(data->fields[num]);
		else return NULL;
	}
	char * column_name(size_t num)
	{
		if(data && num < data->field_count)	
			return data->fields[num].name;
		else return "";
	}
	char * column_org_name(size_t num)
	{
		if(data && num < data->field_count)	
			return data->fields[num].org_name;
		else return "";
	}
	char * column_table(size_t num)
	{
		if(data && num < data->field_count)	
			return data->fields[num].table;
		else return "";
	}
	char * column_org_table(size_t num)
	{
		if(data && num < data->field_count)
			return data->fields[num].org_table;
		else return "";
	}
	enum enum_field_types column_type(size_t num)
	{
		if(data && num < data->field_count)
		return data->fields[num].type;
		else return MYSQL_TYPE_NULL;
	}
};
class Mysql
{
public:
	enum State {INIT, OPENED, CLOSED};
private:
	MYSQL *_mysql;
	State _state;
	void check_error()
	{			
		unsigned int err = ::mysql_errno(_mysql);
		if(err == CR_SERVER_LOST || err == CR_SERVER_GONE_ERROR)
			_state = CLOSED;
	}
public:
	Mysql()
		: _state(INIT)
	{
	}
	bool connect(char const *user = NULL
		, char const *password = NULL
		, char const *host = NULL
		, char const *database = NULL
		, int port = 3306
		)
	{
		if(_state != INIT) return false;
		this->_mysql = mysql_init(NULL);
		if(!_mysql) return false;

		if(::mysql_real_connect(_mysql, host, user, 
			password, database, port, NULL, NULL))
		{
			_state = OPENED;
			return true;
		}
		else return false;
	}
	bool keep_connect()
	{	// if CLOSE, try to reconnect.
		if(_state == OPENED) return true;
		if(_state == INIT) return false;
		MYSQL * temp = ::mysql_init(NULL);
		if(temp == NULL) return false;
		if(::mysql_real_connect(temp, _mysql->host, _mysql->user,
			_mysql->passwd, _mysql->db, _mysql->port,
			_mysql->unix_socket, _mysql->client_flag))
		{
			::mysql_close(_mysql);
			_mysql = temp;
			_state = OPENED;
			return true;
		}
		else
		{
			::mysql_close(temp);
			return false;
		}
	}
	void close()
	{
		if(!_mysql) return;
		::mysql_close(this->_mysql);
		_mysql = NULL;
	}
	bool store(char *sql)
	{
		if(!sql) return false;
		if(!keep_connect()) return false;
		if(::mysql_query(_mysql, sql) != NULL)
		{
			this->check_error();
			return false;
		}
		return true;
	}
	MysqlResult query(char *sql)
	{
		if(!sql || !keep_connect()) return MysqlResult(NULL);
		if(::mysql_query(_mysql, sql) != NULL) 
		{
			this->check_error();
			return MysqlResult(NULL);
		}
		return ::mysql_store_result(_mysql);			
	}
	my_ulonglong get_affected_rows()
	{
		return ::mysql_affected_rows(_mysql);
	}
	unsigned int get_error()
	{
		return ::mysql_errno(_mysql);
	}
	enum State get_state() const
	{
		return _state;
	}
	static void close_library()
	{
		::mysql_library_end();
	}
};
KL_END
#endif // __MYSQL_H_VERSION_KL__
